五个常用算法(一):动态规划

1.从01背包问题说起


有一堆宝石一共n个,现在你身上能装宝石的就只有一个背包,背包的容量为C。把n个宝石排成一排并编上号: 0,1,2,…,n-1。第i个宝石对应的体积和价值分别为V[i]和W[i] 。背包总共也就只能装下体积为C的东西,那你要装下哪些宝石才能获得最大的利益呢?


我们先来看下在这个问题里,动态规划最重要的两个概念:状态和状态转移方程。


假如现在有3个宝石,体积分别为5,4,3;对应的价值为20,10,12;背包的体积是10。这个问题解容易看出是第0、2个宝石,总价值是22。先考虑第2个宝石,有放入和不放入两种选择,选择放入的话,背包剩下空间7,问题变成用空间7的背包装前2个宝石(最优子结构),表示为d(2,7);不放的话变成空间10背包装前2个,表示为d(2,10)。在这里状态d(i,j)表示前i个宝石装到剩余体积为j的背包里能达到的最大价值,状态转移方程式描述状态之间如何转移,原问题状态是d(3,10),如果装入宝石2,那么d(3,10)=d(2,7)+12=32;如果不装,d(3,10)=d(2,10)=30。可以简写为一个式子d(3, 10)=max{ d(2, 10), d(2, 7)+12 } 。即状态转移方程d(i, j)=max{ d(i-1, j), d(i-1,j-V[i-1]) + W[i-1] } 。

代码实现的话是这样:

for(int i=0;i<n;i++){
    for(int j=0;j<C;j++){
        if(i==0)    d[i][j]=0;
        else{
            if(j>W[i]&&V[i]+d[i-1][j-W[i]]>d[i-1][j])    d[i][j]=V[i]+d[i-1][j-W[i]];
            else    d[i][j]=d[i-1][j];
        }
    }
}
解法的时间复杂度、空间复杂度都是O(nC);

时间复杂度没法优化了,可以看出数组d的更新只用到了i-1的值,所以我们只有必要存储数组d[i-1],空间复杂度可以优化一下。


代码如下(注意因为更新j分量需要用到比j小的数值,所以按j递减的顺序更新):

memset(d, 0, sizeof(d));
for(int i=0; i<=n; ++i){
    for(int j=C;j>=0; --j){
        if(j>=V [i]&& i>0) d[j] >?= d[j-V[i]]+W[i];
    }
}


最后贴一个完整代码
/**0-1 knapsack d(i, j)表示前i个物品装到剩余容量为j的背包中的最大重量**/
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;

int main(){
	freopen("data.in", "r", stdin);
	freopen("data.out", "w", stdout);
	int n, C, V = 0, W = 0;
	while(scanf("%d %d", &n, &C) != EOF){
		int* d = (int*)malloc((C+1)*sizeof(int));
		memset(d, 0, (C+1)*sizeof(int));
		
		for(int i=0; i<=n; ++i){
			if(i>0)	scanf("%d %d", &V, &W);
			for(int j=C; j>=0; --j){
				if(j>=V && i>0)	d[j] >?= d[j-V]+W;
			}
		}
		printf("%d\n", d[C]);
		free(d);
	}
	fclose(stdin);
	fclose(stdout);
	return 0;
}



2.动态规划问题思考


能采用动态规划求解的问题的一般要具有3个性质:

    (1) 最优化原理:如果问题的最优解所包含的子问题的解也是最优的,就称该问题具有最优子结构,即满足最优化原理。

    (2) 无后效性:即某阶段状态一旦确定,就不受这个状态以后决策的影响。也就是说,某状态以后的过程不会影响以前的状态,只与当前状态有关。

   (3)有重叠子问题:即子问题之间是不独立的,一个子问题在下一阶段决策中可能被多次使用到。(这个性质是动态规划节省时间复杂度的原因


刷题有感:

首先要确定状态和状态转移方程,难点和关键在于找状态转移方程,参考博客:

http://www.360doc.com/content/13/0601/00/8076359_289597587.shtml


  • 8
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值